#ifndef     __THREADS_H_INCLUDED__
#define     __THREADS_H_INCLUDED__

#include	<condition_variable>
#include	<mutex>
#include	<queue>
#include	<thread>

/**
 * Thread job interface.
 */
struct IThreadJob {
	virtual ~IThreadJob() {}
	virtual void OnRun() = 0;
};

/**
 * Thread pool implementation.
 */
class Threads {
public:
	Threads(size_t workers);
	virtual ~Threads();

	/**
	 * Push thread job into queue.
	 *
	 * \param args... Parameters to create job.
	 * \return Added successfully?
	 */
	template<typename Job, typename ... Args>
	bool AddJob(Args && ... args) {
		{
			std::unique_lock<std::mutex> _(_lock);
			if (!_valid || !_can_add) return false;
			_jobs.push(new Job(std::forward<Args>(args)...));
		}

		_signal.notify_one();
		return true;
	}

	/**
	 * Push allocated job into queue. Note : delete operation will be called
	 * automatically.
	 * 
	 * \param job Allocated job
	 * \return true.
	 */
	bool AddAllocatedJob(IThreadJob * job) {
		{
			std::unique_lock<std::mutex> _(_lock);
			if (!_valid || !_can_add) return false;
			_jobs.push(job);
		}

		_signal.notify_one();
		return true;
	}

	/**
	 * Wait for all jobs finished.
	 */
	void Wait();

private:
	void __WorkerThread();

private:
	bool _valid;
	bool _can_add;
	std::queue<IThreadJob *> _jobs;
	std::queue<std::thread *> _workers;
	std::mutex _lock;
	std::condition_variable _signal;
};

#endif//!   __THREADS_H_INCLUDED__